﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;

namespace Gloo.Management {
    /// <summary>
    /// Contains methods and properties for easily managing a project.
    /// </summary>
    public sealed class Project {

        #region Fields

        private string name;
        private string baseProjPath;
        private string outputPath;
        private string outputName;
        private OutputType outputType;
        private List<File> files;

        #endregion Fields
        
        #region Properties

        /// <summary>
        /// Gets or sets the name of the project.
        /// </summary>
        public string Name {
            get { return name; }
            set { name = value; }
        }

        /// <summary>
        /// Gets the file name of the project.
        /// </summary>
        public string FileName {
            get { return string.Format("{0}{1}.gproj", baseProjPath, name); }
        }

        /// <summary>
        /// Gets or sets the path of the project.
        /// </summary>
        public string ProjectPath {
            get { return baseProjPath; }
            set { baseProjPath = value; }
        }

        /// <summary>
        /// Gets or sets the output name for the project.
        /// </summary>
        public string OutputName {
            get { return outputName; }
            set { outputName = value; }
        }

        /// <summary>
        /// Gets or sets the path where files should be built.
        /// </summary>
        public string OutputPath {
            get { return outputPath; }
            set { outputPath = value; }
        }

        /// <summary>
        /// Gets or sets the output type for the project.
        /// </summary>
        public OutputType OutputType {
            get { return outputType; }
            set { outputType = value; }
        }

        /// <summary>
        /// Gets the list of files included in the project.
        /// </summary>
        public List<File> Files {
            get { return files; }
        }

        #endregion Properties

        #region ctor()

        /// <summary>
        /// Creates a new project.
        /// </summary>
        /// <param name="name">The name of the project.</param>
        /// <param name="path">The base path of the project.</param>
        public Project(string name, string path) {
            this.name = name;
            outputName = name;
            baseProjPath = path;
            outputPath = System.IO.Path.Combine(path, "bin" + File.PathSeparator);
            outputType = OutputType.Executable;
            files = new List<File>();
            Manage(true);
        }

        #endregion ctor()

        #region Methods

        #region Static

        /// <summary>
        /// Loads a project from a file.
        /// </summary>
        /// <param name="file">File to load.</param>
        public static Project FromFile(string file) {
            string name, path, outPath, outName;
            OutputType type;
            List<File> files;
            using (XmlTextReader r = new XmlTextReader(file)) {
                // Get properties
                r.Read();
                if (r.Name != "gproj") throw new Exception("Improper project file.");
                name = r.GetAttribute("name");
                path = r.GetAttribute("path");
                outPath = r.GetAttribute("output");
                outName = r.GetAttribute("outname");
                type = (OutputType)int.Parse(r.GetAttribute("type"));

                // Get files
                files = new List<File>();
                while (r.Read()) {
                    if (r.Name == "file") {
                        File f = new File(r.GetAttribute("path"));
                        f.Action = (BuildAction)int.Parse(r.GetAttribute("action"));
                        files.Add(f);
                    }
                    else if (r.Name != "gproj" && r.Name != "")
                        throw new Exception("Expected 'file' in project file.");
                    
                }
            }

            Project p = new Project(name, path);
            p.OutputPath = outPath;
            p.OutputName = outName;
            p.OutputType = type;
            p.Files.AddRange(files);
            p.Manage(true);
            return p;
        }

        #endregion Static

        #region Public

        /// <summary>
        /// Manages the project by ensuring the project directory exists, and removes files if they do not exist.
        /// </summary>
        /// <param name="save">Specifies whether or not to save the project after managing.</param>
        public void Manage(bool save) {
            // Ensure folder existence
            if (!Directory.Exists(baseProjPath))
                Directory.CreateDirectory(baseProjPath);
            if (!Directory.Exists(outputPath))
                Directory.CreateDirectory(outputPath);

            // Save
            if (save)
                Save();
        }

        /// <summary>
        /// Saves the project.
        /// </summary>
        public void Save() {
            // Simpler than fiddling with XmlTextWriter
            using (StreamWriter sw = new StreamWriter(System.IO.File.Create(FileName))) {
                string projData = @"<gproj name='{0}'
       path='{1}'
       output='{2}'
       outname='{3}'
       type='{4}'>
{5}
</gproj>
";
                string fileData = "";
                foreach (File f in files) {
                    fileData += string.Format("    <file path='{0}' action='{1}' />{2}", f.Path, (int)f.Action, Environment.NewLine);
                }

                sw.Write(string.Format(projData, name, baseProjPath, outputPath, outputName, (int)outputType, fileData));
            }
        }

        #endregion Public

        #endregion Methods

    }

    /// <summary>
    /// Contains an enumeration of all possible output types.
    /// </summary>
    public enum OutputType {
        /// <summary>
        /// Describes an executable (.exe) output type.
        /// </summary>
        Executable = 0,
        /// <summary>
        /// Describes a library (.dll) output type.
        /// </summary>
        Library
    }
}